/*
Class: Tree
A class that manages Tree functions in javascript
*/
function Tree() 
{
    /*
    Variable: Tree
    Keeps the tree strecture
    */
    this.tree;
    this.totalHeight = 0;
    this.totalWidth = 0;
    this.levelHeight = 0;
    this.canvasWidth = 0;
    this.canvasHeight = 0;
    this.boxWidth = 250;
    this.minHorizontalSpace = 50;
    this.boxHeight = 130;
    this.verticalSpace = 50;
    this.compactView = false;
    this.avatarHeight = 65;

    var hiddenNode = { id: null, name: null, level: null, parent: null, StartX: 0, X: 0, Y: 0, PointX1: 0, PointY1: 0, PointX2: 0, PointY2: 0, MinChildWidth: 0, SubNodes: 0, Hidden: !0, NodeOrder: 0, children: [], EPFNumber : '', Designation: '', CompanyName: '', IndirectReporting: '', Grade: '', WorkLevelName: '', style: null, image: '', EmpURL: '', DesignationURL: '', CompanyURL: '' };

    /*
    Function: parseData
    
    parseData from the JSON to Tree Object(s)

    Parameters:

    jsonData - JSON String

    Returns:

    Returns Sets the tree public varible data.

    */
    this.parseData = function (arrData) {
        //if (jsonData == null || jsonData == "" || jsonData == undefined) return;

        //var arrData = jQuery.parseJSON(jsonData);

        this.tree = [];
        this.tree[0] = arrData[0];

        if (arrData.length < 2)
            return;

        for (var i = 1; i < arrData.length; i++) {
            this.insert(arrData[i], arrData[i].parent);
        }
    }

    this.find = function (id) 
    {
	    return find(this.tree[0], id);
	}

	this.getChildren = function (id) {
	    return find(this.tree[0], id).children;
	}


    function find(root, id) 
    {
        var val = null;
        if (root.id == id) {
            val = root;
            return val;
        } else {
            if (root.children.length > 0) {
                for (var i = 0; i < root.children.length; i++) {
                    val = find(root.children[i], id);
                    if (val != null)
                        break;
                }
            }
        }
        return val;
    }

    this.getVisibleParent = function (id) {
        return getVisibleParent(this.tree[0], id);
    }

    function getVisibleParent(root, id) {
        var val = null;
        var node = find(root, id);
        if (!node.Hidden) {
            val = node;
        } else {
            val = find(root, node.parent);
        }  
        return val;
    }
    
    this.insert = function (newNode, parentId)
    {
	    this.tree[0] = insert(this.tree[0], newNode, parentId);
		return this.tree;
	}

    /*
    Function: insert

    Insert nodes to the tree

    Parameters:

    root - root
    newNode - newNode
    parentId - parentId

    Returns:

    Returns true if canvas init successfully.

    */
	function insert(root, newNode, parentId)
	{
	    if (root.id == parentId) {
	        if (root.level == (newNode.level - 1)) {
	            root.children.push(newNode);	           
	        }
	        else 
            {
                root.children.push(insertHiddenNodes(newNode, (newNode.level - root.level)));
            }
            return root;
		}else{
			if(root.children.length > 0){
				 for(var i=0; i < root.children.length; i++){
					root.children[i] = insert(root.children[i], newNode, parentId) ;
				 }
			}
		}		
		return root;
    }


    /*
    Function: insertHiddenNodes
    
    insert hidden nodes to achive the special requirment

    Parameters:

    childNode - childNode
    levelDiff - levelDiff

    Returns:

    Returns true if canvas init successfully.

    */
    function insertHiddenNodes(childNode, levelDiff) {
        var node = $.parseJSON(JSON.stringify(childNode));
        var visibleNodeId = node.id;
        var visibleParentId = node.parent;

       while (levelDiff > 1) {
           var temp = $.parseJSON(JSON.stringify(hiddenNode));
            temp.id = visibleNodeId + "-" + (levelDiff - 1);
            temp.level = node.level - 1;
            levelDiff = levelDiff - 1;
            node.parent = temp.id;
            temp.children.push($.parseJSON(JSON.stringify(node)));
            node = temp;
        }

        node.parent = visibleParentId;
        return node;
    }

    
    this.setLevel = function (level) 
    {
        this.setLevelY(this.tree[0], level);
    }

    this.setLevelY = function (parent, level) {
        //Set the node level
        parent.level = level;
        parent.Y = (parent.level - 1) * (this.boxHeight + this.verticalSpace);

        //Y-coordinates of points to draw lines
        parent.PointY1 = parent.Hidden ? (parent.Y + 2) : parent.Y; //upper point
        parent.PointY2 = parent.Hidden ? (parent.Y -3) : (parent.Y + this.boxHeight); //lower point

        //Calculate the total height based on the number of levels
        if (this.totalHeight < this.levelHeight * level) {
            this.totalHeight = this.levelHeight * level;
            this.canvasHeight = this.totalHeight;
        }

        //Select the opend nodes under this parent
        var nodes = this.getChildren(parent.id);

        //Get the opened nodes number
        parent.SubNodes = nodes.length;

        //Call the child nodes
        for (var i = 0; i < nodes.length; i++) {
            nodes[i].NodeOrder = i + 1;

            nodes[i].MinChildWidth = this.boxWidth + this.minHorizontalSpace;
            this.setLevelY(nodes[i], parent.level + 1);
        }
    }


    /*
    Function: calculateWidth

    Calculate the space between the nodes

    Parameters:

    parent - Parent node

    Returns:

    Returns true if canvas init successfully.

    */
    this.calculateWidth = function(parent) 
    {
        if (parent == undefined || parent == null) {
            return;
        }

        if (this.getChildren(parent.id).length > 0) {

            var nodes = this.getChildren(parent.id);
            var minwidth = 0;

            for (var i = 0; i < nodes.length; i++) {
                this.calculateWidth(nodes[i]);
                minwidth = minwidth + nodes[i].MinChildWidth;
            }

            if (minwidth > parent.MinChildWidth) {

                parent.MinChildWidth = minwidth;
                if (this.canvasWidth < minwidth) {
                    this.canvasWidth = minwidth;
                    this.totalWidth = minwidth;
                }
            }

            var start = parent.startX;

            for (var i = 0; i < nodes.length; i++) {
                nodes[i].startX = start;
                nodes[i].X = nodes[i].StartX + nodes[i].MinChildWidth / 2;
                this.calculateWidth(nodes[i]);
                start = start + nodes[i].MinChildWidth;
            }

            if (nodes.length > 1) {
                parent.X = (nodes[0].X + nodes[nodes.length - 1].X) / 2;
            } else {
                parent.X = nodes[0].X;
            }
        }
    }

    /*
    Function: CalculateX

    Calculate the space between the nodes

    Parameters:

    parent - Parent node

    Returns:

    Returns true if canvas init successfully.

    */
    this.CalculateX = function (parent) {
        if (parent == undefined || parent == null) {
            return;
        }

        if (this.getChildren(parent.id).length > 0) {
            var nodes = this.getChildren(parent.id);

            // Calculate the startX for each node
            var start = parent.StartX;
            for (var i = 0; i < nodes.length; i++) {
                nodes[i].StartX = start;
                nodes[i].X = nodes[i].StartX + nodes[i].MinChildWidth / 2;
                this.CalculateX(nodes[i]);
                start = start + nodes[i].MinChildWidth;
            }

            // realign the parent node to the middle of the child nodes
            if (nodes.length > 1) {
                parent.X = (nodes[0].X + nodes[nodes.length - 1].X) / 2;
            }
            else // root element
            {
                parent.X = nodes[0].X;
            }
        }

        //Y-coordinates of points to draw lines
        parent.PointX1 = parent.X + this.boxWidth / 2;//upper point
        parent.PointX2 = parent.X + this.boxWidth / 2;//lower point
    }
}

